﻿// Inner Fire 游戏引擎库
// 场景分装 - 山水
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-25

#include <dx_graphics.h>

namespace ifire {

void DxGraphics::MountainWater_Update() {
  MountainWater_OnInput();
  AnimateMaterials();
  UpdateObjectCBs();
  UpdateMaterialCBs();
  UpdateMainPassCB();
  // UpdateWaves();
}


void DxGraphics::MountainWater_InitPipeline() {
  MountainWater_InitObjects();

  MountainWater_LoadTextures();
  MountainWater_BuildRootSignature();
  MountainWater_BuildWavesRootSignature();
  MountainWater_BuildPostProcessRootSignature();
  MountainWater_BuildSobelPostProcessRootSignature();
  MountainWater_BuildDescriptorHeaps();
  BuildShaderAndInputLayout();
  BuildMesh_Land();
  MountainWater_BuildGpuWaves();
  BuildMesh_Box();
  BuildMesh_TreeSprites();  
  CreateMeshQuad();
  MountainWater_BuildMaterials();
  MountainWater_BuildRenderItems();
  MountainWater_BuildFrameResources();
  MountainWater_BuildPSOs();
}

void DxGraphics::MountainWater_InitObjects() {
  // GPU波浪对象
  gpu_waves_ = std::make_unique<GpuWaves>(
      device_.Get(), command_list_.Get(), 256, 256, 0.25f, 0.03f, 2.0f, 0.2f);

  // 用于模糊
  blur_filter_ = std::make_unique<BlurFilter>(
      device_.Get(), client_width_, client_height_, DXGI_FORMAT_R8G8B8A8_UNORM);

  // 用于索贝尔算子
  sobel_filter_ = std::make_unique<SobelFilter>(
      device_.Get(), client_width_, client_height_, back_buffer_format_);

  // 用于离屏渲染
  offscreen_rt_ = std::make_unique<RenderTarget>(
      device_.Get(), client_width_, client_height_, back_buffer_format_);
}

void DxGraphics::MountainWater_LoadTextures() {
  auto grassTex = std::make_unique<Texture>();
  grassTex->Name = "GrassTex";
  grassTex->Filename = "Textures/grass.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(grassTex->Filename).c_str(),
      grassTex->Resource, grassTex->UploadHeap));

  auto waterTex = std::make_unique<Texture>();
  waterTex->Name = "WaterTex";
  waterTex->Filename = "Textures/water1.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(waterTex->Filename).c_str(),
      waterTex->Resource, waterTex->UploadHeap));

  auto fenceTex = std::make_unique<Texture>();
  fenceTex->Name = "FenceTex";
  fenceTex->Filename = "Textures/WireFence.dds"; // 这里原来是WoodCrate01
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(fenceTex->Filename).c_str(),
      fenceTex->Resource, fenceTex->UploadHeap));

  auto treeArrayTex = std::make_unique<Texture>();
  treeArrayTex->Name = "TreeArrayTex";
  treeArrayTex->Filename = "Textures/treeArray2.dds";
  HASSERT(DirectX::CreateDDSTextureFromFile12(device_.Get(),
      command_list_.Get(), AnsiToWString(treeArrayTex->Filename).c_str(),
      treeArrayTex->Resource, treeArrayTex->UploadHeap));

  textures_[grassTex->Name] = std::move(grassTex);
  textures_[waterTex->Name] = std::move(waterTex);
  textures_[fenceTex->Name] = std::move(fenceTex);
  textures_[treeArrayTex->Name] = std::move(treeArrayTex);
}

void DxGraphics::MountainWater_BuildRootSignature() {
  // 用于纹理的描述符表
  CD3DX12_DESCRIPTOR_RANGE texTable;
  texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);

  // 用于索贝尔算子的置换纹理
  CD3DX12_DESCRIPTOR_RANGE displacementMapTable;
  displacementMapTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1);

  // 根参数可以是描述符表，根描述符，或根常量
  const int SLOT = 5;
  CD3DX12_ROOT_PARAMETER slot_root_paramater[SLOT];
  // 设置描述符区域的数量，指向描述符区域数组的指针
  slot_root_paramater[0].InitAsDescriptorTable(
      1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL); // 注意这个是描述符表
  slot_root_paramater[1].InitAsConstantBufferView(0);
  slot_root_paramater[2].InitAsConstantBufferView(1);
  slot_root_paramater[3].InitAsConstantBufferView(2);
  slot_root_paramater[4].InitAsDescriptorTable(
      1, &displacementMapTable, D3D12_SHADER_VISIBILITY_ALL);

  // 这个东西是静态的，用于采样器
  auto staticSamplers = GetStaticSamplers();

  // 根签名由一组根参数构成
  // 注意这里和之前的变化，增加了采样器的数量和地址。
  CD3DX12_ROOT_SIGNATURE_DESC root_signature_desc(SLOT, slot_root_paramater,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // 序列化根签名
  ComPtr<ID3DBlob> serialized_root_sign = nullptr;
  ComPtr<ID3DBlob> error_blob = nullptr;
  HRESULT hr = D3D12SerializeRootSignature(&root_signature_desc,
      D3D_ROOT_SIGNATURE_VERSION_1, serialized_root_sign.GetAddressOf(),
      error_blob.GetAddressOf());
  // 常规错误检查
  if (error_blob != nullptr) {
    dxutils::log((char*)error_blob->GetBufferPointer());
  }
  HASSERT(hr);

  // 创建根签名
  HASSERT(device_->CreateRootSignature(0, //
      serialized_root_sign->GetBufferPointer(),
      serialized_root_sign->GetBufferSize(),
      IID_PPV_ARGS(root_signature_.GetAddressOf())));
}

void DxGraphics::MountainWater_BuildWavesRootSignature() {
  CD3DX12_DESCRIPTOR_RANGE uavTable0;
  uavTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);

  CD3DX12_DESCRIPTOR_RANGE uavTable1;
  uavTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 1);

  CD3DX12_DESCRIPTOR_RANGE uavTable2;
  uavTable2.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 2);

  // Root parameter can be a table, root descriptor or root constants.
  CD3DX12_ROOT_PARAMETER slotRootParameter[4];

  // Perfomance TIP: Order from most frequent to least frequent.
  slotRootParameter[0].InitAsConstants(6, 0);
  slotRootParameter[1].InitAsDescriptorTable(1, &uavTable0);
  slotRootParameter[2].InitAsDescriptorTable(1, &uavTable1);
  slotRootParameter[3].InitAsDescriptorTable(1, &uavTable2);

  // A root signature is an array of root parameters.
  CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(
      4, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE);

  // create a root signature with a single slot which points to a descriptor
  // range consisting of a single constant buffer
  ComPtr<ID3DBlob> serializedRootSig = nullptr;
  ComPtr<ID3DBlob> errorBlob = nullptr;
  HRESULT hr =
      D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
          serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

  if (errorBlob != nullptr) {
    dxutils::log((char*)errorBlob->GetBufferPointer());
  }
  ThrowIfFailed(hr);

  ThrowIfFailed(device_->CreateRootSignature(0,
      serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
      IID_PPV_ARGS(waves_signature_.GetAddressOf())));
}

void DxGraphics::MountainWater_BuildPostProcessRootSignature() {

  CD3DX12_DESCRIPTOR_RANGE srvTable;
  srvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);

  CD3DX12_DESCRIPTOR_RANGE uavTable;
  uavTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);

  // 根参数可以是描述符表，根描述符，或根常量
  CD3DX12_ROOT_PARAMETER slot_root_paramater[3];
  // 这12是放置1个radius，11个weight。
  slot_root_paramater[0].InitAsConstants(12, 0);
  slot_root_paramater[1].InitAsDescriptorTable(1, &srvTable);
  slot_root_paramater[2].InitAsDescriptorTable(1, &uavTable);

  // 根签名由一组根参数构成
  // 注意这里和之前的变化，增加了采样器的数量和地址。
  CD3DX12_ROOT_SIGNATURE_DESC root_signature_desc(3, slot_root_paramater, 0,
      nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // 序列化根签名
  ComPtr<ID3DBlob> serialized_root_sign = nullptr;
  ComPtr<ID3DBlob> error_blob = nullptr;
  HRESULT hr = D3D12SerializeRootSignature(&root_signature_desc,
      D3D_ROOT_SIGNATURE_VERSION_1, serialized_root_sign.GetAddressOf(),
      error_blob.GetAddressOf());
  // 常规错误检查
  if (error_blob != nullptr) {
    dxutils::log((char*)error_blob->GetBufferPointer());
  }
  HASSERT(hr);

  // 创建根签名
  HASSERT(device_->CreateRootSignature(0, //
      serialized_root_sign->GetBufferPointer(),
      serialized_root_sign->GetBufferSize(),
      IID_PPV_ARGS(post_processing_signature_.GetAddressOf())));
}

void DxGraphics::MountainWater_BuildSobelPostProcessRootSignature() {

  CD3DX12_DESCRIPTOR_RANGE srvTable0;
  srvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);

  CD3DX12_DESCRIPTOR_RANGE srvTable1;
  srvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1);

  CD3DX12_DESCRIPTOR_RANGE uavTable0;
  uavTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);

  // 根参数可以是描述符表，根描述符，或根常量
  CD3DX12_ROOT_PARAMETER slot_root_paramater[3];
  // 这12是放置1个radius，11个weight。
  slot_root_paramater[0].InitAsDescriptorTable(1, &srvTable0);
  slot_root_paramater[1].InitAsDescriptorTable(1, &srvTable1);
  slot_root_paramater[2].InitAsDescriptorTable(1, &uavTable0);

  auto staticSamplers = GetStaticSamplers();

  // 根签名由一组根参数构成
  // 注意这里和之前的变化，增加了采样器的数量和地址。
  CD3DX12_ROOT_SIGNATURE_DESC root_signature_desc(3, slot_root_paramater,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // 序列化根签名
  ComPtr<ID3DBlob> serialized_root_sign = nullptr;
  ComPtr<ID3DBlob> error_blob = nullptr;
  HRESULT hr = D3D12SerializeRootSignature(&root_signature_desc,
      D3D_ROOT_SIGNATURE_VERSION_1, serialized_root_sign.GetAddressOf(),
      error_blob.GetAddressOf());
  // 常规错误检查
  if (error_blob != nullptr) {
    dxutils::log((char*)error_blob->GetBufferPointer());
  }
  HASSERT(hr);

  // 创建根签名
  HASSERT(device_->CreateRootSignature(0, //
      serialized_root_sign->GetBufferPointer(),
      serialized_root_sign->GetBufferSize(),
      IID_PPV_ARGS(sobel_signature_.GetAddressOf())));
}

void DxGraphics::MountainWater_BuildMaterials() {
  auto grass = std::make_unique<Material>();
  grass->Name = "Grass";
  grass->MatCBIndex = 0;
  grass->DiffuseSrvHeapIndex = 0; // 这里新增加了纹理的SRV
  grass->DiffuseAlbedo = XMFLOAT4(0.2f, 0.6f, 0.2f, 1.0f);
  grass->FresnelR0 = XMFLOAT3(0.01f, 0.01f, 0.01f);
  grass->Roughness = 0.125f;

  auto water = std::make_unique<Material>();
  water->Name = "Water";
  water->MatCBIndex = 1;
  water->DiffuseSrvHeapIndex = 1;
  water->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 0.5f);
  water->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  water->Roughness = 0.0f;

  auto wirefence = std::make_unique<Material>();
  wirefence->Name = "Wirefence";
  wirefence->MatCBIndex = 2;
  wirefence->DiffuseSrvHeapIndex = 2;
  wirefence->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  wirefence->FresnelR0 = XMFLOAT3(0.02f, 0.02f, 0.02f);
  wirefence->Roughness = 0.2f;

  auto treeSprites = std::make_unique<Material>();
  treeSprites->Name = "TreeSprites";
  treeSprites->MatCBIndex = 3;
  treeSprites->DiffuseSrvHeapIndex = 3;
  treeSprites->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  treeSprites->FresnelR0 = XMFLOAT3(0.01f, 0.01f, 0.01f);
  treeSprites->Roughness = 0.125f;

  materials_["Grass"] = std::move(grass);
  materials_["Water"] = std::move(water);
  materials_["Wirefence"] = std::move(wirefence);
  materials_["TreeSprites"] = std::move(treeSprites);
}

void DxGraphics::MountainWater_BuildDescriptorHeaps() {
  // HEAP的大小（用于使项目易读）
  auto HEAP_SIZE_ = cbv_srv_uav_descriptor_size_;

  // RTV的描述符在SWAP_CHAIN的后面，所以需要位移SWAPCHAIN的个数
  const int RTV_OFFSET = SWAP_CHAIN_BUFFER_COUNT;
  // 用于基础的纹理
  const int TEXTURE_COUNT = 4;
  // 用于模糊的纹理
  const int BLUR_COUNT = 4;
  // 用于水的纹理
  int WAVES_COUNT = gpu_waves_->DescriptorCount();
  // 用于索贝尔的纹理
  int SOBEL_COUNT = sobel_filter_->DescriptorCount();
  // 用于额外的离频纹理
  int OFFSCREEN_COUNT = 1;

  // 计算波浪的位移（在第2个），基础纹理是第1个。
  int waves_offset = TEXTURE_COUNT;
  // 索贝尔算子的位移（在第3个）
  int sobel_offset = waves_offset + WAVES_COUNT;
  // 用于模糊的纹理（在第4个）
  int blur_offset = sobel_offset + SOBEL_COUNT;
  // 用于离屏纹理（在第5个）
  int offscreen_offset = blur_offset + BLUR_COUNT;

  // 总共需要的描述符
  int total_count = offscreen_offset + OFFSCREEN_COUNT;

  // 创建用于纹理的SRV描述符
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = total_count;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  HASSERT(device_->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&srv_descriptor_heap_)));

  // 用实际的描述符填充堆。
  CD3DX12_CPU_DESCRIPTOR_HANDLE srv_descriptor(
      srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart());
  auto& grass_tex = textures_["GrassTex"]->Resource;
  auto& water_tex = textures_["WaterTex"]->Resource;
  auto& fence_tex = textures_["FenceTex"]->Resource;
  auto& tree_array_tex = textures_["TreeArrayTex"]->Resource;

  //
  // 用于基础的纹理
  // 填写SRV信息并且创建
  //
  D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
  srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srv_desc.Format = grass_tex->GetDesc().Format;
  srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srv_desc.Texture2D.MostDetailedMip = 0;
  srv_desc.Texture2D.MipLevels = -1;
  device_->CreateShaderResourceView(grass_tex.Get(), &srv_desc, srv_descriptor);

  // 创建下一个，只需要进行OFFSET，移动到下一个堆，再设置格式
  srv_descriptor.Offset(1, HEAP_SIZE_);
  srv_desc.Format = water_tex->GetDesc().Format;
  device_->CreateShaderResourceView(water_tex.Get(), &srv_desc, srv_descriptor);

  // 下一个
  srv_descriptor.Offset(1, HEAP_SIZE_);
  srv_desc.Format = fence_tex->GetDesc().Format;
  device_->CreateShaderResourceView(fence_tex.Get(), &srv_desc, srv_descriptor);

  //
  // 用于树的公告牌。这个是Texture2DArray，需要不同的设置
  //
  // 下一个
  srv_descriptor.Offset(1, HEAP_SIZE_);

  auto desc = tree_array_tex->GetDesc();
  srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
  srv_desc.Format = tree_array_tex->GetDesc().Format;
  srv_desc.Texture2DArray.MostDetailedMip = 0;
  srv_desc.Texture2DArray.MipLevels = -1;
  srv_desc.Texture2DArray.FirstArraySlice = 0;
  srv_desc.Texture2DArray.ArraySize =
      tree_array_tex->GetDesc().DepthOrArraySize;
  device_->CreateShaderResourceView(
      tree_array_tex.Get(), &srv_desc, srv_descriptor);

  // 得到HEAP的地址（用于之后纹理的动态定位）
  auto cpu_address = srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart();
  auto gpu_address = srv_descriptor_heap_->GetGPUDescriptorHandleForHeapStart();
  auto rtv_address = rtv_heap_->GetCPUDescriptorHandleForHeapStart();

// 用于快速建立HANDLE（使程序易读）
#define ADD_ARGS_(offset)                                                      \
  CD3DX12_CPU_DESCRIPTOR_HANDLE(cpu_address, offset, HEAP_SIZE_),              \
      CD3DX12_GPU_DESCRIPTOR_HANDLE(gpu_address, offset, HEAP_SIZE_)

  // 用于波浪的纹理
  gpu_waves_->BuildDescriptors(ADD_ARGS_(waves_offset), HEAP_SIZE_);

  // 用于索贝尔算子的纹理
  sobel_filter_->BuildDescriptors(ADD_ARGS_(sobel_offset), HEAP_SIZE_);

  // 填充用于模糊的纹理
  blur_filter_->BuildDescriptors(ADD_ARGS_(blur_offset), HEAP_SIZE_);

  // 用于离屏纹理
  auto h_offscreen = CD3DX12_CPU_DESCRIPTOR_HANDLE(
      rtv_address, RTV_OFFSET, rtv_descriptor_size_);
  offscreen_rt_->BuildDescriptors(ADD_ARGS_(offscreen_offset), h_offscreen);
}

void DxGraphics::BuildMesh_Land() {
  GeometryGenerator generator;
  GeometryGenerator::MeshData grid =
      generator.CreateGrid(160.0f, 160.0f, 50, 50);

  // 获得我们所需要的顶点元素，并且利用高度函数计算每个顶点的高度值
  // 顶点的颜色要基于它们的高度而定，所以图像中才有看起来像沙质的沙滩
  // 山腰处的植被，以及山峰处的积雪
  std::vector<Vertex> vertices(grid.Vertices.size());
  for (size_t i = 0; i < grid.Vertices.size(); i++) {
    auto& p = grid.Vertices[i].Position;
    auto& v = vertices[i];
    v.Pos = p;
    v.Pos.y = GetHillsHeight(p.x, p.z);
    v.Normal = GetHillsNormal(p.x, p.z);
    v.TexC = grid.Vertices[i].TexC;
  }

  const UINT vertex_buffer_size = (UINT)vertices.size() * sizeof(Vertex);

  std::vector<std::uint16_t> indices = grid.GetIndices16();
  const UINT index_buffer_size = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<SimpleMesh>();
  geo->Name = "LandGeo";

  // 创建顶点的缓冲区与Uploader
  HASSERT(D3DCreateBlob(vertex_buffer_size, &geo->VertexBufferCPU));
  CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), //
      vertices.data(), vertex_buffer_size);

  // 创建索引的缓冲区和Uploader
  HASSERT(D3DCreateBlob(index_buffer_size, &geo->IndexBufferCPU));
  CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), //
      indices.data(), index_buffer_size);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vertex_buffer_size, geo->VertexBufferUploader);
  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), index_buffer_size, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vertex_buffer_size;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = index_buffer_size;

  SimpleSubmesh submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["grid"] = submesh;
  geometries_["LandGeo"] = std::move(geo);
}

void DxGraphics::MountainWater_BuildGpuWaves() {
  GeometryGenerator geoGen;
  GeometryGenerator::MeshData grid = geoGen.CreateGrid(
      160.0f, 160.0f, gpu_waves_->RowCount(), gpu_waves_->ColumnCount());

  std::vector<Vertex> vertices(grid.Vertices.size());
  for (size_t i = 0; i < grid.Vertices.size(); ++i) {
    vertices[i].Pos = grid.Vertices[i].Position;
    vertices[i].Normal = grid.Vertices[i].Normal;
    vertices[i].TexC = grid.Vertices[i].TexC;
  }

  std::vector<std::uint32_t> indices = grid.Indices32;

  UINT vbByteSize = gpu_waves_->VertexCount() * sizeof(Vertex);
  UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint32_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "WaterGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), //
      vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), //
      indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R32_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SimpleSubmesh submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["grid"] = submesh;

  geometries_["WaterGeo"] = std::move(geo);
}

void DxGraphics::BuildMesh_Waves() {
  // 3 indices per face
  std::vector<std::uint16_t> indices(3 * waves_->TriangleCount());
  assert(waves_->VertexCount() < 0x0000ffff);

  // Iterate over each quad.
  int m = waves_->RowCount();
  int n = waves_->ColumnCount();
  int k = 0;
  for (int i = 0; i < m - 1; ++i) {
    for (int j = 0; j < n - 1; ++j) {
      indices[k] = i * n + j;
      indices[k + 1] = i * n + j + 1;
      indices[k + 2] = (i + 1) * n + j;

      indices[k + 3] = (i + 1) * n + j;
      indices[k + 4] = i * n + j + 1;
      indices[k + 5] = (i + 1) * n + j + 1;

      k += 6; // next quad
    }
  }

  UINT vbByteSize = waves_->VertexCount() * sizeof(Vertex);
  UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<SimpleMesh>();
  geo->Name = "WaterGeo";

  // Set dynamically.
  geo->VertexBufferCPU = nullptr;
  geo->VertexBufferGPU = nullptr;

  HASSERT(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SimpleSubmesh submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["grid"] = submesh;

  geometries_["WaterGeo"] = std::move(geo);
}

void DxGraphics::MountainWater_BuildRenderItems() {
  auto waves_item = std::make_unique<RenderItem>();
  waves_item->World = IDENTITY_4X4;
  XMStoreFloat4x4(&waves_item->TexTransform, //
      XMMatrixScaling(5.0f, 5.0f, 1.0f));
  waves_item->DisplacementMapTexelSize.x = 1.0f / gpu_waves_->ColumnCount();
  waves_item->DisplacementMapTexelSize.y = 1.0f / gpu_waves_->RowCount();
  waves_item->GridSpatialStep = gpu_waves_->SpatialStep();
  waves_item->ObjCBIndex = 0;
  waves_item->Mat = materials_["Water"].get();
  waves_item->Geo = geometries_["WaterGeo"].get();
  waves_item->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  waves_item->IndexCount = waves_item->Geo->DrawArgs["grid"].IndexCount;
  waves_item->StartIndexLocation =
      waves_item->Geo->DrawArgs["grid"].StartIndexLocation;
  waves_item->BaseVertexLocation =
      waves_item->Geo->DrawArgs["grid"].BaseVertexLocation;

  waves_item_ = waves_item.get();

  render_layer[(int)RenderLayer::GpuWaves].push_back(waves_item.get());

  auto grid_item = std::make_unique<RenderItem>();
  grid_item->World = IDENTITY_4X4;
  XMStoreFloat4x4(&grid_item->TexTransform, XMMatrixScaling(5.0f, 5.0f, 1.0f));
  grid_item->ObjCBIndex = 1;
  grid_item->Mat = materials_["Grass"].get();
  grid_item->Geo = geometries_["LandGeo"].get();
  grid_item->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  grid_item->IndexCount = grid_item->Geo->DrawArgs["grid"].IndexCount;
  grid_item->StartIndexLocation =
      grid_item->Geo->DrawArgs["grid"].StartIndexLocation;
  grid_item->BaseVertexLocation =
      grid_item->Geo->DrawArgs["grid"].BaseVertexLocation;

  render_layer[(int)RenderLayer::Opaque].push_back(grid_item.get());

  auto box_item = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&box_item->World, XMMatrixTranslation(3.0f, 2.0f, -9.0f));
  box_item->ObjCBIndex = 2;
  box_item->Mat = materials_["Wirefence"].get();
  box_item->Geo = geometries_["BoxGeo"].get();
  box_item->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  box_item->IndexCount = box_item->Geo->DrawArgs["box"].IndexCount;
  box_item->StartIndexLocation =
      box_item->Geo->DrawArgs["box"].StartIndexLocation;
  box_item->BaseVertexLocation =
      box_item->Geo->DrawArgs["box"].BaseVertexLocation;

  render_layer[(int)RenderLayer::AlphaTested].push_back(box_item.get());

  auto treeSpritesRitem = std::make_unique<RenderItem>();
  treeSpritesRitem->World = MathHelper::Identity4x4();
  treeSpritesRitem->ObjCBIndex = 3;
  treeSpritesRitem->Mat = materials_["TreeSprites"].get();
  treeSpritesRitem->Geo = geometries_["TreeSpritesGeo"].get();
  treeSpritesRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
  treeSpritesRitem->IndexCount =
      treeSpritesRitem->Geo->DrawArgs["points"].IndexCount;
  treeSpritesRitem->StartIndexLocation =
      treeSpritesRitem->Geo->DrawArgs["points"].StartIndexLocation;
  treeSpritesRitem->BaseVertexLocation =
      treeSpritesRitem->Geo->DrawArgs["points"].BaseVertexLocation;

  render_layer[(int)RenderLayer::AlphaTestedTreeSprites].push_back(
      treeSpritesRitem.get());

  items_.push_back(std::move(waves_item));
  items_.push_back(std::move(grid_item));
  items_.push_back(std::move(box_item));
  items_.push_back(std::move(treeSpritesRitem));
}
float DxGraphics::GetHillsHeight(float x, float z) const {
  return 0.3f * (z * sinf(0.1f * x) + x * cosf(0.1f * z));
}

XMFLOAT3 DxGraphics::GetHillsNormal(float x, float z) const {
  // 因为地形曲面由函数y=f(x,z)给出，所以可以通过微积分知识来直接计算法线量，
  // 而不必再用法线平均值方法。
  // 针对曲面上的每一个点，可以通过偏导数在+x与+z方向建立两个切向量。
  // 这两个向量都位于曲面点的切平面上，求这两个向量的叉积即可得到对应点处的法向量。
  // 推导过程位于教程。

  // n = (-df/dx, 1, -df/dz);
  float df_dx = -0.03f * z * cosf(0.1f * x) - 0.3f * cosf(0.1f * z);
  float df_dz = -0.3f * sinf(0.1f * x) + 0.03f * x * sinf(0.1f * z);
  XMFLOAT3 normal(df_dx, 1.0f, df_dz);

  // 归一化
  XMVECTOR unit_normal = XMVector3Normalize(XMLoadFloat3(&normal));
  XMStoreFloat3(&normal, unit_normal);

  return normal;
}
void DxGraphics::BuildMesh_TreeSprites() {
  struct TreeSpriteVertex {
    XMFLOAT3 Pos;
    XMFLOAT2 Size;
  };

  static const int treeCount = 16;
  std::array<TreeSpriteVertex, 16> vertices;
  for (UINT i = 0; i < treeCount; ++i) {
    float x = MathHelper::RandF(-45.0f, 45.0f);
    float z = MathHelper::RandF(-45.0f, 45.0f);
    // 这个必须和之前的山连动，这样才能确定位置。
    float y = GetHillsHeight(x, z);

    // 树比上要高一点点（因为是正中的顶点）
    y += 8.0f;

    vertices[i].Pos = XMFLOAT3(x, y, z);
    vertices[i].Size = XMFLOAT2(20.0f, 20.0f);
  }

  std::array<std::uint16_t, 16> indices = {
      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(TreeSpriteVertex);
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "TreeSpritesGeo";

  HASSERT(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  HASSERT(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(TreeSpriteVertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SubmeshGeometry submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["points"] = submesh;

  geometries_["TreeSpritesGeo"] = std::move(geo);
}

void DxGraphics::MountainWater_BuildPSOs() {
  // TIP1:
  // PSO的创建非常消耗性能。所以最好在初始化的时候就创建。
  // TIP2:
  // 尽可能减少改变PSO状态的次数，如果能用一个PSO绘制出所有的物体，绝不用第2个。

  //
  // 用于不透明的对象
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaque_desc;
  ZeroMemory(&opaque_desc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));

  opaque_desc.InputLayout = {
      input_layout_.data(), static_cast<UINT>(input_layout_.size())};
  opaque_desc.pRootSignature = root_signature_.Get();
  opaque_desc.VS = {
      reinterpret_cast<BYTE*>(shaders_["StandardVS"]->GetBufferPointer()),
      shaders_["StandardVS"]->GetBufferSize()};
  opaque_desc.PS = {
      reinterpret_cast<BYTE*>(shaders_["OpaquePS"]->GetBufferPointer()),
      shaders_["OpaquePS"]->GetBufferSize()};
  opaque_desc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  // opaque_desc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
  opaque_desc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaque_desc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaque_desc.SampleMask = UINT_MAX;
  opaque_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaque_desc.NumRenderTargets = 1;
  opaque_desc.RTVFormats[0] = back_buffer_format_;
  opaque_desc.SampleDesc.Count = GetMsaaSampleCount();
  opaque_desc.SampleDesc.Quality = GetMsaaSampleQuality();
  opaque_desc.DSVFormat = depth_stencil_format_;
  // 用于不透明的对象
  HASSERT(device_->CreateGraphicsPipelineState(
      &opaque_desc, IID_PPV_ARGS(&pipelines_["Opaque"])));

  //
  // 用于透明的Pipeline
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC transparent_desc = opaque_desc;
  // 用于透明混合
  D3D12_RENDER_TARGET_BLEND_DESC blend_desc = {};
  blend_desc.BlendEnable = true;
  blend_desc.LogicOpEnable = false;
  // blend_desc.SrcBlend = D3D12_BLEND_SRC_COLOR;
  // blend_desc.DestBlend = D3D12_BLEND_DEST_COLOR;
  // 这点我总没明白，Water这个图片里面没Alpha啊，他凭什么能合成呢？
  // 后来我找到了材质里面的diffuse，他在那里设计alpha。但Shader中从哪儿取的？
  blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
  blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
  blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
  blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE;
  blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO;
  blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
  blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP;
  blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
  transparent_desc.BlendState.RenderTarget[0] = blend_desc;
  HASSERT(device_->CreateGraphicsPipelineState(
      &transparent_desc, IID_PPV_ARGS(&pipelines_["Transparent"])));

  //
  // 用于AlphaTest的Pipeline
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC alpha_test_desc = opaque_desc;
  alpha_test_desc.PS = {
      reinterpret_cast<BYTE*>(shaders_["AlphaTestPS"]->GetBufferPointer()),
      shaders_["AlphaTestPS"]->GetBufferSize()};
  // 需要关闭背面剔除
  alpha_test_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
  HASSERT(device_->CreateGraphicsPipelineState(
      &alpha_test_desc, IID_PPV_ARGS(&pipelines_["AlphaTested"])));

  //
  // 用于绘制波浪
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC wavesRenderPSO = transparent_desc;
  wavesRenderPSO.VS = {
      reinterpret_cast<BYTE*>(shaders_["WavesVS"]->GetBufferPointer()),
      shaders_["WavesVS"]->GetBufferSize()};
  ThrowIfFailed(device_->CreateGraphicsPipelineState(
      &wavesRenderPSO, IID_PPV_ARGS(&pipelines_["WavesRender"])));

  //
  // 用于生成搅动波浪
  //
  D3D12_COMPUTE_PIPELINE_STATE_DESC wavesDisturbPSO = {};
  wavesDisturbPSO.pRootSignature = waves_signature_.Get();
  wavesDisturbPSO.CS = {
      reinterpret_cast<BYTE*>(shaders_["WavesDisturbCS"]->GetBufferPointer()),
      shaders_["WavesDisturbCS"]->GetBufferSize()};
  wavesDisturbPSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
  ThrowIfFailed(device_->CreateComputePipelineState(
      &wavesDisturbPSO, IID_PPV_ARGS(&pipelines_["WavesDisturb"])));

  //
  // 用于更新波浪
  //
  D3D12_COMPUTE_PIPELINE_STATE_DESC wavesUpdatePSO = {};
  wavesUpdatePSO.pRootSignature = waves_signature_.Get();
  wavesUpdatePSO.CS = {
      reinterpret_cast<BYTE*>(shaders_["WavesUpdateCS"]->GetBufferPointer()),
      shaders_["WavesUpdateCS"]->GetBufferSize()};
  wavesUpdatePSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
  ThrowIfFailed(device_->CreateComputePipelineState(
      &wavesUpdatePSO, IID_PPV_ARGS(&pipelines_["WavesUpdate"])));

  //
  // 用于树公告牌的PSO
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC treeSpritePsoDesc = opaque_desc;
  treeSpritePsoDesc.VS = {
      reinterpret_cast<BYTE*>(shaders_["TreeSpriteVS"]->GetBufferPointer()),
      shaders_["TreeSpriteVS"]->GetBufferSize()};
  treeSpritePsoDesc.GS = {
      reinterpret_cast<BYTE*>(shaders_["TreeSpriteGS"]->GetBufferPointer()),
      shaders_["TreeSpriteGS"]->GetBufferSize()};
  treeSpritePsoDesc.PS = {
      reinterpret_cast<BYTE*>(shaders_["TreeSpritePS"]->GetBufferPointer()),
      shaders_["TreeSpritePS"]->GetBufferSize()};
  treeSpritePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
  treeSpritePsoDesc.InputLayout = {
      tree_sprite_input_layout_.data(), (UINT)tree_sprite_input_layout_.size()};
  treeSpritePsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
  // TIP：这里使用这个，可以使图片的边缘Alpha得到裁剪更好，是利用MSAA做的效果。
  // 但是实际上显示也不尽人意。
  treeSpritePsoDesc.BlendState.AlphaToCoverageEnable = true;

  HASSERT(device_->CreateGraphicsPipelineState(
      &treeSpritePsoDesc, IID_PPV_ARGS(&pipelines_["TreeSprites"])));

  //
  // 用于水平的模糊
  //
  D3D12_COMPUTE_PIPELINE_STATE_DESC horzBlurPSO = {};
  horzBlurPSO.pRootSignature = post_processing_signature_.Get();
  horzBlurPSO.CS = {
      reinterpret_cast<BYTE*>(shaders_["HorzBlurCS"]->GetBufferPointer()),
      shaders_["HorzBlurCS"]->GetBufferSize()};
  horzBlurPSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
  HASSERT(device_->CreateComputePipelineState(
      &horzBlurPSO, IID_PPV_ARGS(&pipelines_["HorzBlur"])));

  //
  // 用于垂直的模糊
  //
  D3D12_COMPUTE_PIPELINE_STATE_DESC vertBlurPSO = {};
  vertBlurPSO.pRootSignature = post_processing_signature_.Get();
  vertBlurPSO.CS = {
      reinterpret_cast<BYTE*>(shaders_["VertBlurCS"]->GetBufferPointer()),
      shaders_["VertBlurCS"]->GetBufferSize()};
  vertBlurPSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
  HASSERT(device_->CreateComputePipelineState(
      &vertBlurPSO, IID_PPV_ARGS(&pipelines_["VertBlur"])));

  //
  // 用于混合的PostProcess
  //
  D3D12_GRAPHICS_PIPELINE_STATE_DESC compositePSO = opaque_desc;
  compositePSO.pRootSignature = sobel_signature_.Get();

  // 需要关闭深度测试
  compositePSO.DepthStencilState.DepthEnable = false;
  compositePSO.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
  compositePSO.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;

  compositePSO.VS = {
      reinterpret_cast<BYTE*>(shaders_["CompositeVS"]->GetBufferPointer()),
      shaders_["CompositeVS"]->GetBufferSize()};
  compositePSO.PS = {
      reinterpret_cast<BYTE*>(shaders_["CompositePS"]->GetBufferPointer()),
      shaders_["CompositePS"]->GetBufferSize()};
  ThrowIfFailed(device_->CreateGraphicsPipelineState(
      &compositePSO, IID_PPV_ARGS(&pipelines_["Composite"])));

  //
  // 用于索贝尔算子
  //
  D3D12_COMPUTE_PIPELINE_STATE_DESC sobelPSO = {};
  sobelPSO.pRootSignature = sobel_signature_.Get();
  sobelPSO.CS = {
      reinterpret_cast<BYTE*>(shaders_["SobelCS"]->GetBufferPointer()),
      shaders_["SobelCS"]->GetBufferSize()};
  sobelPSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
  ThrowIfFailed(device_->CreateComputePipelineState(
      &sobelPSO, IID_PPV_ARGS(&pipelines_["Sobel"])));
}

void DxGraphics::MountainWater_BuildFrameResources() {
  FrameResourceDesc desc = {};
  desc.PassCount = 1;
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());
  desc.WaveVertexCount = 0;

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}

void DxGraphics::MountainWater_Draw() {
  auto cmd = command_list_.Get();

  Draw_Reset();
  Draw_SetHeaps();

  // 在这里执行顶点的更新。因为产生的图要做为后续的输入
  MountainWater_UpdateWavesGPU();

  cmd->SetPipelineState(pipelines_["Opaque"].Get());

  Draw_SetViewport();

  // 将离屏纹理更改为用作渲染目标输出。
  // 这里和以前的渲染不一样，因为都是用离屏纹理，所以不能调用原来的BackBuffer了。
  auto offscreen_render = CD3DX12_RESOURCE_BARRIER::Transition(
      offscreen_rt_->Resource(), D3D12_RESOURCE_STATE_GENERIC_READ,
      D3D12_RESOURCE_STATE_RENDER_TARGET);
  cmd->ResourceBarrier(1, &offscreen_render);

  // Clear the back buffer and depth buffer.
  auto depth_stencil = GetDepthStencilView();

  // 瞎猫碰到死耗子：教程中一直出错误：
  // D3D12 WARNING:
  // ID3D12CommandList::ClearRenderTargetView: The application did not pass any
  // clear value to resource creation. The clear operation is typically slower
  // as a result; but will still clear to the desired value.
  // 然后我用这个clearValue中的这个Color才消除了这个警告。但我也不知道为什么这两个不同。
  cmd->ClearRenderTargetView(
      offscreen_rt_->mhCpuRtv, offscreen_rt_->clearValue.Color, 0, nullptr);
  cmd->ClearDepthStencilView(depth_stencil,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
  // Specify the buffers we are going to render to.
  cmd->OMSetRenderTargets(1, &offscreen_rt_->mhCpuRtv, true, &depth_stencil);

  Draw_SetRootSignature();

  // 设置MainPass缓冲区（使用根常量）
  auto pass_cb = current_frame_resource_->PassCB->Resource();
  cmd->SetGraphicsRootConstantBufferView(2, pass_cb->GetGPUVirtualAddress());
  cmd->SetGraphicsRootDescriptorTable(4, gpu_waves_->DisplacementMap());

  // !!!绘制对象项目
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Opaque]);

  // 绘制AlphaTest项目（它居然可以直接这样换Pipeline！）
  cmd->SetPipelineState(pipelines_["AlphaTested"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::AlphaTested]);

  cmd->SetPipelineState(pipelines_["TreeSprites"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::AlphaTestedTreeSprites]);

  // 绘制透明项目
  cmd->SetPipelineState(pipelines_["Transparent"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::Transparent]);

  cmd->SetPipelineState(pipelines_["WavesRender"].Get());
  DrawRenderItems(cmd, render_layer[(int)RenderLayer::GpuWaves]);

  // 改变离屏纹理，将其用于输出
  auto set_input = CD3DX12_RESOURCE_BARRIER::Transition(
      offscreen_rt_->Resource(), D3D12_RESOURCE_STATE_RENDER_TARGET,
      D3D12_RESOURCE_STATE_GENERIC_READ);
  cmd->ResourceBarrier(1, &set_input);
  // 执行索贝尔算子
  sobel_filter_->Execute(cmd, sobel_signature_.Get(), pipelines_["Sobel"].Get(),
      offscreen_rt_->Srv());

  // 将渲染对象设置回后备缓冲区
  // Indicate a state transition on the resource usage.
  auto back_render = CD3DX12_RESOURCE_BARRIER::Transition(GetBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  cmd->ResourceBarrier(1, &back_render);

  // Specify the buffers we are going to render to.
  auto back_buffer = GetBackBufferView();
  cmd->OMSetRenderTargets(1, &back_buffer, true, &depth_stencil);

  // 进行混合计算
  cmd->SetGraphicsRootSignature(sobel_signature_.Get());
  cmd->SetPipelineState(pipelines_["Composite"].Get());
  cmd->SetGraphicsRootDescriptorTable(0, offscreen_rt_->Srv());
  cmd->SetGraphicsRootDescriptorTable(1, sobel_filter_->OutputSrv());
  DrawFullscreenQuad(cmd);

  // 进行模糊
  if (is_blur_enable_) {
    blur_filter_->Execute(cmd, post_processing_signature_.Get(),
        pipelines_["HorzBlur"].Get(), pipelines_["VertBlur"].Get(),
        GetBackBuffer(), 4);
  }

  Draw_Commit();
}

void DxGraphics::CreateMeshQuad() {

  // 获得我们所需要的顶点元素，并且利用高度函数计算每个顶点的高度值
  // 顶点的颜色要基于它们的高度而定，所以图像中才有看起来像沙质的沙滩
  // 山腰处的植被，以及山峰处的积雪
  std::vector<Vertex> vertices(6);
  const UINT vertex_buffer_size = (UINT)vertices.size() * sizeof(Vertex);
  std::vector<std::uint16_t> indices = {0, 1, 2, 3, 4, 5};
  const UINT index_buffer_size = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<SimpleMesh>();
  geo->Name = "Quad";

  // 创建顶点的缓冲区与Uploader
  HASSERT(D3DCreateBlob(vertex_buffer_size, &geo->VertexBufferCPU));
  CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), //
      vertices.data(), vertex_buffer_size);

  // 创建索引的缓冲区和Uploader
  HASSERT(D3DCreateBlob(index_buffer_size, &geo->IndexBufferCPU));
  CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), //
      indices.data(), index_buffer_size);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vertex_buffer_size, geo->VertexBufferUploader);
  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), index_buffer_size, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vertex_buffer_size;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = index_buffer_size;

  SimpleSubmesh submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["grid"] = submesh;
  geometries_["Quad"] = std::move(geo);

  empty_vertex_buffer_ = {};
  empty_index_buffer_ = {};
}

void DxGraphics::DrawFullscreenQuad(ID3D12GraphicsCommandList* cmdList) {
  // 我在这里给了任意一个顶点。其实是一个片。但是，这里面的顶点只要有6个就行，其它的根本不需要
  // 因为Shader里面没有用到。但是，我也不用去改Shader，因为Shader里面已经有它所需要的一切。
  // 换句话说，这就是一个DUMMY的MESH。没有任何意义。只要索引数一样就行。
  // 这个东西完全就是用来消除警告的。因为如果你传入空的IndexBuffer，会一直弹警告：
  // D3D12 WARNING: ID3D12CommandList::DrawInstanced: Element [0] in the current
  // Input Layout's declaration references input slot 0, but there is no Buffer
  // bound to this slot. This is OK, as reads from an empty slot are defined to
  // return 0. It is also possible the developer knows the data will not be used
  // anyway. This is only a problem if the developer actually intended to bind
  // an input Buffer here.  [ EXECUTION WARNING #202:
  // COMMAND_LIST_DRAW_VERTEX_BUFFER_NOT_SET]
  // 教程根本不管警告的事情。把人坑出翔。

  auto geo = geometries_["Quad"].get();
  auto& item = geo->DrawArgs["grid"];

  // 绑定顶点与索引
  auto vertex_view = geo->VertexBufferView();
  cmdList->IASetVertexBuffers(0, 1, &vertex_view);
  auto index_view = geo->IndexBufferView();
  cmdList->IASetIndexBuffer(&index_view);

  cmdList->DrawIndexedInstanced(item.IndexCount, 1, 0, 0, 0);

  // 由于我们在着色器中基于SV_VertexID构建顶点，因此将IA阶段设置为Null。
  // cmdList->IASetVertexBuffers(0, 1, &empty_vertex_buffer_);
  // cmdList->IASetIndexBuffer(&empty_index_buffer_);
  // cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

  // cmdList->DrawInstanced(6, 1, 0, 0);
}

void DxGraphics::MountainWater_OnInput() {
  const float dt = g_timer.fixedDeltaTime;

  if (GetAsyncKeyState('B') & 0x8000) {
    is_blur_enable_ = true;
  } else {
    is_blur_enable_ = false;
  }
}

void DxGraphics::MountainWater_UpdateWavesGPU() {
  // Every quarter second, generate a random wave.
  static float t_base = 0.0f;
  if ((g_timer.TotalTime() - t_base) >= 0.25f) {
    t_base += 0.25f;

    int i = MathHelper::Rand(4, gpu_waves_->RowCount() - 5);
    int j = MathHelper::Rand(4, gpu_waves_->ColumnCount() - 5);

    float r = MathHelper::RandF(1.0f, 2.0f);

    gpu_waves_->Disturb(command_list_.Get(), waves_signature_.Get(),
        pipelines_["WavesDisturb"].Get(), i, j, r);
  }

  // Update the wave simulation.
  gpu_waves_->SetAllAccess(command_list_.Get());
  gpu_waves_->Update(command_list_.Get(), waves_signature_.Get(),
      pipelines_["WavesUpdate"].Get());
  gpu_waves_->SetAllRead(command_list_.Get());
}

} // namespace ifire